#include "rtc.h"


void music_menu(){
	int i;
	music_queue = newGQueue(200, sizeof(Note)); 

	Note menuTheme[] = {{D6, 200}, {E6, 200},{F6, 200},{A6, 200}, {C7, 200},{As6, 200},{G6, 200}, {As6, 200},{A6, 300},{D6, 300},
					 {G6, 200},{F6, 200},{Ds6, 200}, {D6, 250},{A5, 250}, {A5, 200}, {As5,200}, {C6, 250}, {D7, 150}, {C7, 150},    
					 {As6, 150},{A6, 150}, {G6, 150}, {F6, 150}, {Ds6, 150},{D6, 150}, {C6, 250},{D6, 200}, {E6, 200},{F6, 200},
					 {A6, 200}, {C7, 200},{As6, 200},{G6, 200}, {As6, 200},{A6, 300},{D6, 300},{G6, 200},{F6, 200},{Ds6, 200}, 
					 {D6, 250},{A5, 250}, {A5, 200}, {As5,200}, {C6, 250} ,{D6, 200}, {D6, 200},  {F6, 200},  {F6, 200}, {As6, 200},
					 {As6, 200}, {Ds7, 200}, {Ds7, 200},{A5, 200}, {A5, 200}, {As5, 200}, {C6, 200},{C6, 200}, {As5, 200},  {A5, 200} };
					 
	for(i=0; i<60; i++)
		putGQueue(music_queue, &menuTheme[i]);
		
	
}
//volatile int rtc_ints = 0, 
volatile int rtc_upd_ints = 0, rtc_per_ints = 0, rtc_alarm_ints = 0;

int pause = 50; //pausa entre notas

//Rotina de interrupcao
void rtc_isr(void)
{
	static int dur; //duracao da nota / intervalo
	Byte cause = read_rtc(RTC_STAT_C);
	
	if (cause & RTC_UF) // seconds interrupt
		rtc_upd_ints++;
	
	if (cause & RTC_AF) // alarm interrupt
		rtc_alarm_ints++;
	
	if (cause & RTC_PF){ // periodic interrupt
		switch(music){
			case OFF:
				speaker_off();
				break;
			case BEEP: //Tocar apenas uma nota (BEEP)
				timer_load(TIMER_2, notes_frequency[Do5]);
				dur = 20;
				speaker_on();
				music = PLAY;
				break;
			case PLAY:
				if(--dur <= 0){ //Terminou a nota
					speaker_off();
					dur = pause;
					music = PAUSE;
				}
				break;
			case PAUSE:
				if(--dur <= 0)
					music = STOPPED;
				break;
			case STOPPED:
				if ( (note = (Note *) getGQueue(music_queue)) != NULL) {					
					timer_load(TIMER_2, notes_frequency[note->freq]);
					dur = note->dur;
					speaker_on();
					music = PLAY;
				}
				else
					music_menu();
				break;
		}
		rtc_per_ints++;
	}
	outportb(PIC1_CMD, EOI);
	outportb(PIC2_CMD, EOI);
}

/** converts BCD (Binary Coded Decimal) to decimal
 */
Byte bcd2dec(Byte i)
{
	return (i & 0x0F) + 10 * ((i >> 4) & 0x0F);
}


/** converts decimal to BCD (Binary Coded Decimal)
 */
Byte dec2bcd(Byte i)
{
	return (i % 10) + (((i / 10) % 10) << 4);
}


void rtc_int_enable()
{
	Byte stat_b = read_rtcv(RTC_STAT_B);
	write_rtcv(RTC_STAT_B, stat_b | RTC_UIE | RTC_AIE | RTC_PIE);

}


void rtc_int_enable_periodic()
{
	//programar RTC para gerar periodic ints a um rate de 1024KHz
	write_rtc(RTC_STAT_A, (read_rtc(RTC_STAT_A) & 0xF0) | 0x06);
	
	//activar RTC periodic ints
	write_rtc(RTC_STAT_B, (read_rtc(RTC_STAT_B) & 0x8F) | RTC_PIE);
	read_rtc(RTC_STAT_C);
}


void rtc_int_disable()
{
	write_rtc(RTC_STAT_B, read_rtc(RTC_STAT_B) & ~(RTC_PIE | RTC_AIE | RTC_UIE));
}


/** Wait until data in rtc is valid.
 * Data is valid until +/- 240 usec after this function returns,
 * thus a read/write can be done to the RTC within this time period
 */
void rtc_valid()
{
	disable();
	outportb(RTC_ADDR_REG, RTC_STAT_A);
	while( inportb(RTC_DATA_REG) & RTC_UIP);
	enable();
}


/** Returns rtc data from I/O address add. Doesn't check valid bit
 */
Byte read_rtc(Byte add)
{
	disable();
	outportb(RTC_ADDR_REG, add);
	Byte resultado = inportb(RTC_DATA_REG);
	enable();
	return resultado;
}


/** Returns rtc data from I/O address add. Check valid bit
 */
Byte read_rtcv(Byte add)
{
	rtc_valid();
	return read_rtc(add);
}


/** Write value to I/O address add. Doesn't check valid bit
 */
void write_rtc(Byte add, Byte value)
{
	disable();
	//printf("wrt=%x\n",value);
	outportb(RTC_ADDR_REG, add);
	outportb(RTC_DATA_REG, value);
	enable();
}


/** Write value to I/O address add. Check valid bit
 */
void write_rtcv(Byte add, Byte value)
{
	rtc_valid();
	write_rtc(add, value);
}


/** Read RTC stored time
 *
 * Uses read_rtcv() and bcd2dec()
 */
void rtc_read_time(RTC_TIME* time)
{
	// Coloca o bit SET a 1
	outportb(RTC_ADDR_REG, RTC_STAT_B);
	Byte bit_set = inportb(RTC_DATA_REG);
	outportb(RTC_ADDR_REG, RTC_STAT_B); 
	outportb(RTC_DATA_REG, (bit_set | RTC_SET) );
	
	time->hour = bcd2dec( read_rtcv(HOUR) );
	time->min = bcd2dec( read_rtcv(MIN) );
	time->sec = bcd2dec( read_rtcv(SEC) );
	
	outportb(RTC_ADDR_REG, RTC_STAT_B);
	Byte bit_set2 = inportb(RTC_DATA_REG);
	outportb(RTC_ADDR_REG, RTC_STAT_B); 
	outportb(RTC_DATA_REG, (bit_set2 & ~RTC_SET) );
}


/** Read RTC stored date
 *
 * Uses read_rtcv() and bcd2dec()
 */
void rtc_read_date(RTC_DATE* date)
{
	outportb(RTC_ADDR_REG, RTC_STAT_B);
	Byte bit_set = inportb(RTC_DATA_REG);
	outportb(RTC_ADDR_REG, RTC_STAT_B); 
	outportb(RTC_DATA_REG, (bit_set | RTC_SET) );
	
	date->day = bcd2dec( read_rtcv(MONTH_DAY) );
	date->month = bcd2dec( read_rtcv(MONTH) );
	date->year = bcd2dec( read_rtcv(YEAR) );
	
	outportb(RTC_ADDR_REG, RTC_STAT_B);
	Byte bit_set2 = inportb(RTC_DATA_REG);
	outportb(RTC_ADDR_REG, RTC_STAT_B); 
	outportb(RTC_DATA_REG, (bit_set2 & ~RTC_SET) );
}


/** Read RTC stored alarm
 *
 * Uses read_rtcv() and bcd2dec()
 */
void rtc_read_alarm(RTC_TIME* alarm)
{
	outportb(RTC_ADDR_REG, RTC_STAT_B);
	Byte bit_set = inportb(RTC_DATA_REG);
	outportb(RTC_ADDR_REG, RTC_STAT_B); 
	outportb(RTC_DATA_REG, (bit_set | RTC_SET) );
	
	alarm->hour = bcd2dec( read_rtcv(HOUR_ALARM) );
	alarm->min = bcd2dec( read_rtcv(MIN_ALARM) );
	alarm->sec = bcd2dec( read_rtcv(SEC_ALARM) );
	
	outportb(RTC_ADDR_REG, RTC_STAT_B);
	Byte bit_set2 = inportb(RTC_DATA_REG);
	outportb(RTC_ADDR_REG, RTC_STAT_B); 
	outportb(RTC_DATA_REG, (bit_set2 & ~RTC_SET) );
}


/** Write alarm to RTC
 *
 * Uses write_rtcv() and dec2bcd()
 */
void rtc_write_alarm(RTC_TIME* time)
{
	// Hora
	outportb(RTC_ADDR_REG, HOUR_ALARM);
	outportb(RTC_DATA_REG, dec2bcd(time->hour) );
	// Minutos
	outportb(RTC_ADDR_REG, MIN_ALARM);
	outportb(RTC_DATA_REG, dec2bcd(time->min) );
	// Segundo
	outportb(RTC_ADDR_REG, SEC_ALARM);
	outportb(RTC_DATA_REG, dec2bcd(time->sec) );
}



